﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;

namespace Conference
{
    /// <summary>
    /// Службовий клас для потокової обробки аудіофрагментів, що надходять с мікрофону.
    /// </summary>
    public class AudioSinkDer : AudioSink
    {
        public AudioFormat audioFormat; //формат аудіо

        public MemoryStream stream, raw_stream; //потоки для збереження аудіофрагментів

        private byte[] wavFileHeader; //массив заголовку WAVE-структури

        private byte[] buffer = new byte[4096]; //буферний масив

        private int read; //лічильник для зчитування інформації в поток

        public byte[] sdata; //допоміжна змінна для запису аудиоінформації в потік

        public long st, sd; //момент запису та довжина аудіофрагмента відповідно

        /// <summary>
        /// Процедура отримання повного WAVE-заголовку.
        /// </summary>
        /// <param name="audioLength">Довжина аудіофрагменту.</param>
        /// <param name="audioFormat">Формат аудіофрагменту.</param>
        /// <returns></returns>
        private static byte[] GetWavFileHeader(long audioLength, AudioFormat audioFormat)
        {
            //Ініціалізація допоміжного потоку
            MemoryStream stream = new MemoryStream(44);

            // Записі RIFF-заголовока
            stream.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }, 0, 4);

            // Резервування місця для подальшого запису інформації
            stream.Write(BitConverter.GetBytes((UInt32)(audioLength + 44 - 8)), 0, 4);

            // Запис WAVE-заголовку
            stream.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }, 0, 4);

            // Запис fmt-заголовку
            stream.Write(new byte[] { 0x66, 0x6D, 0x74, 0x20 }, 0, 4);

            // Запис номеру PCM
            stream.Write(BitConverter.GetBytes((UInt32)16), 0, 4);

            // Відключення компресії
            stream.Write(BitConverter.GetBytes((UInt16)1), 0, 2);

            // Кількість каналів
            stream.Write(BitConverter.GetBytes((UInt16)audioFormat.Channels), 0, 2);

            // Частота аудиофрагментів
            stream.Write(BitConverter.GetBytes((UInt32)audioFormat.SamplesPerSecond), 0, 4);

            // Частота байтів
            stream.Write(BitConverter.GetBytes((UInt32)
                ((audioFormat.SamplesPerSecond *
                audioFormat.Channels * audioFormat.BitsPerSample) / 8)), 0, 4);

            // Вирівнювання блоків
            stream.Write(BitConverter.GetBytes((UInt16)
                ((audioFormat.Channels * audioFormat.BitsPerSample) / 8)), 0, 2);

            // Кількість бітів на аудіофрагмент
            stream.Write(BitConverter.GetBytes((UInt16)audioFormat.BitsPerSample), 0, 2);

            // Службова інформація
            stream.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }, 0, 4);

            // Довжина залишку файлу
            stream.Write(BitConverter.GetBytes((UInt32)audioLength), 0, 4);

            return (stream.GetBuffer());
        }

        /// <summary>
        /// Запуск захоплення аудіо.
        /// </summary>
        protected override void OnCaptureStarted()
        {
            raw_stream = new MemoryStream(); //ініціалізація допоміжного потоку

            stream = new MemoryStream(); //ініціалізація допоміжного потоку
        }

        /// <summary>
        /// Емуляція запуску захоплення аудіо.
        /// </summary>
        public void Start()
        {
            OnCaptureStarted();
        }

        /// <summary>
        /// Емуляція зупинення захоплення аудіо.
        /// </summary>
        public void Stop()
        {
            OnCaptureStopped();
        }

        /// <summary>
        /// Зупинення захоплення аудіо.
        /// </summary>
        protected override void OnCaptureStopped()
        {
            wavFileHeader = GetWavFileHeader(raw_stream.Length, audioFormat); //отримання WAVE-заголовку

            stream.Write(wavFileHeader, 0, wavFileHeader.Length); //запис в потік

            read = 0; //ініціалізація лічільнику байтів

            raw_stream.Seek(0, SeekOrigin.Begin); //встановлення початкової позиції

            while ((read = raw_stream.Read(buffer, 0, buffer.Length)) > 0) //дописування в потік
            {
                stream.Write(buffer, 0, read);
            }

        }

        /// <summary>
        /// Емуляція процедури обробки аудіофрагментів.
        /// </summary>
        /// <param name="st">Час початку захоплення аудіо.</param>
        /// <param name="sd">Довжина аудиофрагменту.</param>
        /// <param name="temp_array">Масив байтів з аудіоінформацією.</param>
        public void ProcessSamples(long st, long sd, byte[] temp_array)
        {
            this.OnSamples(st, sd, temp_array);
        }

        /// <summary>
        /// Обробка зміни аудиоформату пристроєм.
        /// </summary>
        /// <param name="audioFormat">Новий аудіоформат.</param>
        protected override void OnFormatChange(AudioFormat audioFormat)
        {
            if (this.audioFormat == null)
            {
                this.audioFormat = audioFormat;
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

        /// <summary>
        /// Процедура обробки аудіофрагментів.
        /// </summary>
        /// <param name="sampleTime">Час початку захоплення аудіо.</param>
        /// <param name="sampleDuration">Довжина аудиофрагменту.</param>
        /// <param name="sampleData">Масив байтів з аудіоінформацією.</param>
        protected override void OnSamples(long sampleTime, long sampleDuration, byte[] sampleData)
        {
            st = sampleTime;
            sd = sampleDuration;
            sdata = sampleData;
            raw_stream.Write(sampleData, 0, sampleData.Length);
        }
    }
}
